package jmine.tec.di.injector.impl;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;

import jmine.tec.annotations.Unmodifiable;
import jmine.tec.di.DependencyInjectionMessages;
import jmine.tec.di.injector.DependencyInjector;
import jmine.tec.di.injector.Injector;
import jmine.tec.di.metadata.DependencyMetaData;
import jmine.tec.di.scope.TypedScope;
import jmine.tec.di.type.TypeConvertionException;
import jmine.tec.di.validation.StaticValidator;
import bancosys.tec.exception.CompoundMessageHolder;
import bancosys.tec.exception.LocalizedMessageHolder;

/**
 * @author MarceloT
 */
@Unmodifiable
public final class DependencyInjectorImpl implements DependencyInjector {

    private final Injector[] allInjectors;

    /**
     * @param allInjectors todos os injectors
     */
    public DependencyInjectorImpl(Injector[] allInjectors) {
        super();
        this.allInjectors = sortInjectors(allInjectors);
    }

    /**
     * Organiza os injectors de forma que os resolvers estaticos fiquem no comeco
     * 
     * @param injectors {@link Injector}
     * @return {@link Injector}
     */
    private static Injector[] sortInjectors(Injector[] injectors) {
        Arrays.sort(injectors, new Comparator<Injector>() {
            public int compare(Injector o1, Injector o2) {
                if (o1.getDependencyMetaData().getResolver().isStatic()) {
                    return -1;
                }
                return 1;
            }
        });
        return injectors;
    }

    /**
     * Injeta tudo
     * 
     * @param target the target
     * @param properties the properties
     * @throws TypeConvertionException e
     */
    public void inject(Object target, TypedScope properties) throws TypeConvertionException {
        List<LocalizedMessageHolder> errors = new ArrayList<LocalizedMessageHolder>();
        for (Injector injector : this.allInjectors) {
            try {
                injector.doInject(target, properties);
            } catch (TypeConvertionException e) {
                errors.add(e.getLocalizedMessageHolder());
            }
        }
        if (!errors.isEmpty()) {
            throw new TypeConvertionException(DependencyInjectionMessages.MULTIPLE_ERRORS.create(new CompoundMessageHolder("\n")
                    .addMessages(errors)));
        }
    }

    /**
     * Valida parametros requeridos
     * 
     * @param scope {@link TypedScope}
     * @return {@link List} de {@link String}
     */
    public List<String> validateRequiredProperties(TypedScope scope) {
        DependencyMetaData[] meta = new DependencyMetaData[this.allInjectors.length];
        for (int i = 0; i < meta.length; i++) {
            meta[i] = this.allInjectors[i].getDependencyMetaData();
        }
        return StaticValidator.validateSimple(scope, meta);
    }

    /**
     * @return the allInjectors
     */
    public Injector[] getAllInjectors() {
        Injector[] copy = new Injector[this.allInjectors.length];
        System.arraycopy(this.allInjectors, 0, copy, 0, copy.length);
        return copy;
    }

}
